import axios from 'axios'
import { isEmpty } from 'lodash-es'
import qs from 'qs'

import config from './config'
import { REQUEST_TIMEOUT, RESULT_CODE } from './constants'
import onAppError from './onAppError'
import { Session } from './storage'

const { apiUrlPrefix } = config

const instance = axios.create({
  timeout: REQUEST_TIMEOUT,
  transformResponse: [
    data => {
      /* eslint no-param-reassign:0 */
      if (typeof data === 'string') {
        try {
          data = JSON.parse(data, (k, v) => {
            if (typeof v === 'string') {
              return v.trim()
              // 将null转化为undefined
            } else if (v === null) {
              return undefined
            }
            return v
          })
        } catch (e) {
          console.log('transformResponse', e)
          /* Ignore */
        }
      }
      return data
    },
  ],
})

/**
 *
 * @param {*} options
 * @returns
 */
const fetch = options => {
  const { url, method = 'get', data = {}, requestConfig: _requestConfig } = options
  const token = Session.getToken()
  const headers = {}
  if (token) {
    headers['X-Auth-Token'] = token
  }
  if (url.includes(apiUrlPrefix)) {
    headers['cors-request'] = 'cts|octopus-cors'
  }

  /** @type {import('axios').AxiosRequestConfig} */
  const requestConfig = {
    headers,
    ..._requestConfig,
  }

  switch (method.toLowerCase()) {
    case 'get':
      requestConfig.params = data
      requestConfig.paramsSerializer = params => qs.stringify(params, { arrayFormat: 'repeat' })
      return axios.get(`${url}`, requestConfig)
    case 'post':
      return axios.post(url, data, requestConfig)
    case 'delete':
      return axios.delete(`${url}${!isEmpty(data) ? `?${qs.stringify(data)}` : ''}`, requestConfig)
    case 'put':
      return axios.put(url, data, requestConfig)
    default:
      return axios(Object.assign({}, options, { data: data }))
  }
}

/**
 * 预处理返回的数据
 * @param {*} response
 * @returns
 */
export const resolveResponse = response => {
  let { data } = response

  // 如果是文件导出，则返回整个response
  if (response.config.responseType === 'blob') {
    return response
  }
  const { code } = data
  if (code === RESULT_CODE.REQUEST_SUCCESS_CODE) {
    return data
  }
  if (data.message === 'Conflict') {
    data.message = '数据重复'
  }
  if (data.message === 'COMMODITY DELETE FAIL') {
    data.message = '商品已关联订单，不可删除'
  }
  throw response
}

const fetchRequest = async options => fetch(options).then(resolveResponse)

/**
 * 带错误捕获，适用于非effects环境
 * @returns {function(*): *}
 * @param service
 * @param data
 * @param throwError
 */
const requestWithTry = async (service, data, throwError = false) => {
  let res
  try {
    if (service) {
      res = service(data)
      if (res instanceof Promise) {
        res = await res
      }
    }
    return res
  } catch (e) {
    if (throwError) {
      throw e
    } else {
      onAppError(e)
      return Promise.reject(e)
    }
  }
}

/**
 * @param {string} url
 * @param { 'get' | 'post' | 'delete' | 'put' } method
 * @param {import('axios').AxiosRequestConfig} requestConfig 单位毫秒，默认1分钟
 * @returns
 */
const generateRequest = (url, method = 'post', requestConfig = {}) => {
  let source = axios.CancelToken.source()
  const fn = async (data = {}) => {
    const relUrl = url.replace(/[{](\w+)[}]/gi, (str, $1) => {
      if (data[$1] !== undefined) {
        const value = String(data[$1])
        delete data[$1]
        return value
      }
      return ''
    })
    return fetchRequest({
      url: relUrl,
      method,
      data,
      requestConfig: {
        ...requestConfig,
        cancelToken: source.token,
      },
    })
  }
  fn.cancel = () => {
    source.cancel()
    source = axios.CancelToken.source()
  }
  return fn
}

export { generateRequest, instance, requestWithTry }
export default fetchRequest
